﻿/*
 * Copyright 2012 Rene Prost, Registrite ja Infosüsteemide Keskus 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
using System;
using System.IO;
using System.Xml;
using Xtee.Core.Envelope.Header;
using Xtee.Core.Types;

namespace Xtee.Core.Envelope
{

    [global::Xtee.Core.Attributes.XmlTypeAttribute(Name = "Envelope", Namespace = "http://producers.kis.xtee.riik.ee/producer/kis", Target = typeof(SoapEnvelope))]
    public class SoapEnvelopeSerializer : global::Xtee.Core.Types.AbstractXteeSerializable
    {

        protected ISoapEnvelope clazz;
        private IFaultDetailSerializer _faultDetailSerializer;
        public ISoapEnvelope Envelope { get { return this.clazz; } }
        public SoapEnvelopeSerializer(ISoapEnvelope clazz)
        {
            this.clazz = clazz;
        }
        public SoapEnvelopeSerializer() : this(new SoapEnvelope()) { }

        public SoapEnvelopeSerializer Set(IFaultDetailSerializer detailSerializer)
        {
            _faultDetailSerializer = detailSerializer;
            return this;
        }

        public override void WriteXml(global::Xtee.Core.IXteeXmlWriter Writer)
        {
            base.WriteXml(Writer);

            if (clazz.Header != null && !(this.clazz.Body is Fault))
            {
                if (Envelope.HeaderStream != null)
                {
                    //When responding, the service MUST copy all the header fields from the request to the
                    //response in the exact same sequence with the exact same values. 
                    Envelope.HeaderStream.Position = 0;
                    var reader=new StreamReader(Envelope.HeaderStream);
                    Writer.WriteRaw(reader.ReadToEnd());
                }
                else
                {
                    Writer.WriteStartElement("SOAP-ENV", "Header", "http://schemas.xmlsoap.org/soap/envelope/");
                    GetMetaData(this.clazz.Header).NewSerializer(clazz.Header).WriteXml(Writer);
                    Writer.WriteEndElement();
                }
                
            }

            Writer.WriteStartElement("SOAP-ENV", "Body", "http://schemas.xmlsoap.org/soap/envelope/");
            if (clazz.Body != null)
            {
                SerializationMetadata meta = GetMetaData(this.clazz.Body);
                AbstractXteeSerializable serializer = meta.NewSerializer(clazz.Body);
                if (meta.SerializedType.FullName == typeof (Fault).FullName)
                {
                    ((FaultSerializer) serializer).Set(_faultDetailSerializer);
                    Writer.WriteStartElement("SOAP-ENV", "Fault", "http://schemas.xmlsoap.org/soap/envelope/");
                }
                else
                {
                    var prefix=Writer.LookupPrefix(meta.TypeQualifiedName.Namespace);
                    Writer.WriteStartElement(string.IsNullOrEmpty(prefix)?"tns":prefix, clazz.OperationName, meta.TypeQualifiedName.Namespace);
                }
                   

                serializer.WriteXml(Writer);
                Writer.WriteEndElement();
            }
            else
            {
                throw new global::Xtee.Core.Exceptions.NillableNotAllowedException("Nullable is not allowed for element : Body");
            }
            Writer.WriteEndElement();
        }

        public override void ReadXml(global::Xtee.Core.IXteeXmlReader Reader)
        {
            base.ReadXml(Reader);
            if (Reader.LocalName == "Header")
            {
                //When responding, the service MUST copy all the header fields from the request to the
                //response in the exact same sequence with the exact same values. 

                Envelope.HeaderStream = Reader.ReadNode();
                var headerReader = XteeXmlReader.Create(Envelope.HeaderStream, new XteeXmlReaderSettings(false), null);
                headerReader.MoveToContent();

                if (headerReader.IsEmptyElement == false)
                {
                    headerReader.ReadStartElement("Header", "http://schemas.xmlsoap.org/soap/envelope/");
                    GetMetaData(this.clazz.Header).NewSerializer(clazz.Header).ReadXml(headerReader);
                    headerReader.ReadEndElement();
                }

            }

            if (Reader.LocalName == "Body" && Reader.NamespaceURI == "http://schemas.xmlsoap.org/soap/envelope/")
            {
                if (Reader.IsElementNil())
                {
                    throw new global::Xtee.Core.Exceptions.NillableNotAllowedException(">>Envelope>Body");
                }

                if ((Reader.IsEmptyElement == false))
                {
                    Reader.ReadStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/");
                    Reader.ReadStartElement(Reader.LocalName, Reader.NamespaceURI);
                    if (Reader.NodeType != XmlNodeType.EndElement)
                        GetMetaData(this.clazz.Body).NewSerializer(clazz.Body).ReadXml(Reader);

                    Reader.ReadEndElement();
                    Reader.ReadEndElement();
                }
                else
                {
                    Reader.Skip();
                }
            }
            else
            {
                throw new global::Xtee.Core.Exceptions.UnExpectedElementException(">>Envelope>Body",
                    new System.Xml.XmlQualifiedName(Reader.LocalName, Reader.NamespaceURI).ToString());
            }

        }
        /**/
        private void Validate()
        {
            if (clazz.Header == null)
                throw new NullReferenceException("clazz header must be instanciated instance");
            else //check if nimi is present
            {

                object serviceCode = clazz.Header.GetServiceName();
                if (clazz.OperationName != "listMethods" && serviceCode == null)
                    throw new NullReferenceException("Soap Header nimi element cannot be null!");
            }


            if (clazz.Body == null)
                throw new NullReferenceException("Soap envelope Body must be instanciated instance");
        }
    }
}
